package org.jjj.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.jjj.basic.config.RegisterNoticeRabbitmqConfig;
import org.jjj.basic.constants.WxConstants;
import org.jjj.basic.utils.HttpClientUtils;
import org.jjj.basic.utils.MD5Utils;
import org.jjj.basic.utils.ResultAjax;
import org.jjj.user.domain.Logininfo;
import org.jjj.user.domain.User;
import org.jjj.user.domain.Wxuser;
import org.jjj.user.dto.AccountRegisterDTO;
import org.jjj.user.dto.RegisterNoticeDTO;
import org.jjj.user.dto.RetrievepasswordDTO;
import org.jjj.user.dto.WechatLoginDTO;
import org.jjj.user.mapper.LogininfoMapper;
import org.jjj.user.mapper.UserMapper;
import org.jjj.user.mapper.WxuserMapper;
import org.jjj.user.service.IUserService;
import org.jjj.basic.service.impl.BaseServiceImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jiangjianjun
 * @since 2022-11-15
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements IUserService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    /**
     * 处理门户网站用户注册信息
     * @param dto
     */
    @SuppressWarnings("SingleStatementInBlock")
    @Override
    @Transactional
    public ResultAjax register(AccountRegisterDTO dto) {
        String mobile = dto.getMobile();
        String smsCode = dto.getSmsCode();
        String username = dto.getUsername();
        String password = dto.getPassword();
        String confirmPassword = dto.getConfirmPassword();
        //1.前端参数非空校验
        if(StringUtils.isEmpty(mobile)||StringUtils.isEmpty(smsCode)||
           StringUtils.isEmpty(username)||StringUtils.isEmpty(password)||
           StringUtils.isEmpty(confirmPassword)){
            return ResultAjax.me().setSuccess(false).setMessage("请填写完整注册信息后再提交！");
        }
        //2.确认两次输入的密码是否一致
        if (!password.trim().equals(confirmPassword.trim())){
            return ResultAjax.me().setSuccess(false).setMessage("两次密码输入不一致，请重新输入！");
        }
        //3.输入的手机验证码是否正确
        String key = "register_sms_code_"+mobile;//存入redis中数据的key
        //从redis中取出之前存入的手机短信验证码
        String smsCodeOfRedis = (String)redisTemplate.opsForValue().get(key);
        String phoneCode = smsCodeOfRedis.split(":")[0];
        if (StringUtils.isEmpty(smsCodeOfRedis)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码已过期,请重新获取！");
        }
        if (!smsCode.equals(phoneCode)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码输入错误！");
        }
        //4.用户名是否已经存在
        //根据用户名去数据库查询
        User userByDB = userMapper.selectByUsername(username);
        if (Objects.nonNull(userByDB)){
            return ResultAjax.me().setSuccess(false).setMessage("该用户名已存在！");
        }
        //5.保存注册信息到logininfo表中
        Logininfo logininfo = new Logininfo();
        logininfo.setDisable(1);//1可用，0禁用
        logininfo.setPhone(mobile);
        logininfo.setType(1);//1是门户网站用户，0是后台管理平台用户
        logininfo.setUsername(username);
        //密码要进行加盐加密处理
        String salt = String.valueOf(UUID.randomUUID());//随机生成盐值
        //加密后的密码
        String encrypByMd5 = MD5Utils.encrypByMd5("123" + salt + "jjj" + password);
        logininfo.setSalt(salt);
        logininfo.setPassword(encrypByMd5);
        //存入logininfo表中
        logininfoMapper.insert(logininfo);
        //6.保存注册用户信息到user表中
        User user = new User();
        user.setCreatetime(new Date());
        user.setLogininfoId(logininfo.getId());
        user.setPassword(encrypByMd5);
        user.setPhone(mobile);
        user.setSalt(salt);
        user.setState(2);//2表示已激活
        user.setUsername(username);
        //存入数据库
        userMapper.insert(user);
        //7.注册成功后应该给注册用户发送：短信通知或邮件通知
        //通过消息队列发送短信通知
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.sms",new RegisterNoticeDTO(mobile,"短信通知：您好，您的账号注册已完成！"));
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.email",new RegisterNoticeDTO(mobile,"邮件通知：您好，您的账号注册已完成！"));
        return ResultAjax.me();
    }

    /**
     * 绑定微信账号提交数据到后台做业务处理
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public ResultAjax userBindWechat(WechatLoginDTO dto) {
        String mobile = dto.getMobile();
        String smsCode = dto.getSmsCode();
        String openid = dto.getOpenid();
        String accessToken = dto.getAccessToken();
        //1.参数判空
        if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(smsCode)){
            return ResultAjax.me().setSuccess(false).setMessage("手机号或手机验证码未填写，请填写后再绑定！");
        }
        if (StringUtils.isBlank(openid)||StringUtils.isBlank(accessToken)){
            return ResultAjax.me().setSuccess(false).setMessage("二维码已过期，请刷新二维码后重新扫码登录！");
        }
        //2.判断输入的手机验证码是否正确
        String key = "binder_sms_code_"+mobile;//存入redis中数据的key
        //从redis中取出之前存入的手机短信验证码
        String smsCodeOfRedis = (String)redisTemplate.opsForValue().get(key);
        String phoneCode = smsCodeOfRedis.split(":")[0];
        if (StringUtils.isEmpty(smsCodeOfRedis)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码已过期,请重新获取！");
        }
        if (!smsCode.equals(phoneCode)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码输入错误！");
        }
        //3.判断手机号是否已经注册为该平台用户,并进行绑定
        User user = userMapper.getUserByPhone(mobile);
        Logininfo logininfo=null;
        Wxuser wxuser=null;
        if (Objects.nonNull(user)){
            //3.1已经注册成为平台用户
            //3.1.1判断是否绑定了平台的其他微信用户
            wxuser = wxuserMapper.getWxuserByUserId(user.getId());
            if (Objects.nonNull(wxuser)){
                //3.1.1.1该手机号用户已绑定该平台其他微信用户账号，提示用户请先解绑后重新绑定
                return ResultAjax.me().setSuccess(false).setMessage("该手机号用户已绑定其他微信用户账号，请先解绑后重新绑定!");
            }else {
                //3.1.1.2手机号注册的用户已存在但还没绑定微信用户，直接绑定，然后免密登录
                //先去微信平台获取微信用户的信息然后转化为自己平台数据库里的微信用户信息
                wxuser = buildWxuser(openid,accessToken);
                //这一步是进行微信账号绑定
                wxuser.setUserId(user.getId());
                //存入到数据库
                wxuserMapper.insert(wxuser);
                //获取当前登录用户的logininfo，登录之后需要返给前端页面需要展示登录用户
                logininfo = logininfoMapper.getLogininfoByUserId(user.getId());
            }
        }else {
            //3.2此手机号还未注册成为本平台用户
            //3.2.1要先注册成为本平台用户：将用户信息保存到logininfo表，user表
            //3.2.1.1封装logininfo对象，保存到logininfo表中
            logininfo = buildLogininfo(mobile);
            logininfoMapper.insert(logininfo);
            //3.2.1.2封装user对象并保存到数据库中
            user=logininfo2User(logininfo);
            userMapper.insert(user);
            //3.2.2去微信平台获取微信对象，进行绑定，并保存一份到自己的微信用户表中
            wxuser = buildWxuser(openid,accessToken);
            //将微信用户进行绑定
            wxuser.setUserId(user.getId());
            wxuserMapper.insert(wxuser);
        }
        //4.免密登录：无论哪种情况，如果绑定成功都要做免密登录
        return handleLoginSuccess(logininfo);
    }

    /**
     * 找回密码
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public ResultAjax retrievepassword(RetrievepasswordDTO dto) {
        String confirmPassword = dto.getConfirmPassword();
        String mobile = dto.getMobile();
        String password = dto.getPassword();
        String smsCode = dto.getSmsCode();
        Integer type = dto.getType();

        //1.前端参数非空校验
        if(StringUtils.isEmpty(mobile)||StringUtils.isEmpty(smsCode)||
                type==null||StringUtils.isEmpty(password)||
                StringUtils.isEmpty(confirmPassword)){
            return ResultAjax.me().setSuccess(false).setMessage("请填写完整信息后再提交！");
        }
        //2.确认两次输入的密码是否一致
        if (!password.trim().equals(confirmPassword.trim())){
            return ResultAjax.me().setSuccess(false).setMessage("两次密码输入不一致，请重新输入！");
        }
        //3.输入的手机验证码是否正确
        String key = "retrievepassword_sms_code_"+mobile;//存入redis中数据的key
        //从redis中取出之前存入的手机短信验证码
        String smsCodeOfRedis = (String)redisTemplate.opsForValue().get(key);
        String phoneCode = smsCodeOfRedis.split(":")[0];
        if (StringUtils.isEmpty(smsCodeOfRedis)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码已过期,请重新获取！");
        }
        if (!smsCode.equals(phoneCode)){
            return ResultAjax.me().setSuccess(false).setMessage("手机短信验证码输入错误！");
        }
        //4.根据手机号判断用户是否存在
        User user = userMapper.getUserByPhone(mobile);
        if (Objects.isNull(user)){
            return ResultAjax.me().setSuccess(false).setMessage("该手机号用户不存在，请直接注册登录！");
        }
        //5.给用户信息和登录信息重新设置密码
        Logininfo logininfo = logininfoMapper.getRetrievepasswordLogininfoByPhone(dto);
        String salt = logininfo.getSalt();
        String encrypByMd5 = MD5Utils.encrypByMd5("123" + salt + "jjj" + password);
        logininfo.setPassword(encrypByMd5);
        logininfoMapper.update(logininfo);
        user.setPassword(encrypByMd5);
        userMapper.update(user);
        return ResultAjax.me();
    }

    /**
     * 生成一份手机用户信息
     * @param logininfo
     * @return
     */
    private User logininfo2User(Logininfo logininfo) {
        User user = new User();
        //调用工具类中的方法，将logininfo中相同的字段直接复制给user中的相同字段
        BeanUtils.copyProperties(logininfo,user);
        //不同的字段单独赋值
        user.setState(2);
        user.setLogininfoId(logininfo.getId());
        user.setCreatetime(new Date());
        return user;
    }

    /**
     * 封装一份logininfo对象
     * @param mobile
     * @return
     */
    private Logininfo buildLogininfo(String mobile) {
        Logininfo logininfo = new Logininfo();
        logininfo.setUsername(UUID.randomUUID().toString());//给用户一个默认的用户名
        logininfo.setType(1);
        logininfo.setPhone(mobile);
        logininfo.setDisable(1);
        //加盐
        String salt = UUID.randomUUID().toString();
        logininfo.setSalt(salt);
        //给用户一个初始密码
        String password = MD5Utils.encrypByMd5("123" + salt + "jjj" + 123456);
        logininfo.setPassword(password);
        return logininfo;
    }

    /**
     * 去微信平台获取微信用户的信息然后转化为自己平台数据库里的微信用户信息
     * @param openid
     * @param accessToken
     * @return
     */
    private Wxuser buildWxuser(String openid, String accessToken) {
        //1.去微信平台获取微信用户信息
        String wxuserStr = HttpClientUtils.httpGet(WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openid));
        //2.将取到的wxuserStr字符串转为wxuser对象
        Wxuser wxuser = wxuserStr2wxuserObj(wxuserStr);
        return wxuser;
    }

    /**
     * 将取到的wxuserStr字符串转为wxuser对象
     * @param wxuserStr
     * @return
     */
    private Wxuser wxuserStr2wxuserObj(String wxuserStr) {
        //1.将字符串转化为json对象
        JSONObject jsonObject = JSONObject.parseObject(wxuserStr);
        //2.判断一下获取的微信用户是否已经过期
        if (StringUtils.isBlank(jsonObject.getString("openid"))){
            throw new RuntimeException("当前微信登录已过期，请重新扫码登录！");
        }
        Wxuser wxuser = new Wxuser();
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        String country = jsonObject.getString("country");
        String province = jsonObject.getString("province");
        String city = jsonObject.getString("city");
        wxuser.setAddress(country+province+city);
        return wxuser;
    }

    /**
     * 免密登录方法
     * @param logininfo
     * @return
     */
    private ResultAjax handleLoginSuccess(Logininfo logininfo){
        //5.记录logininfo到redis中，后面登录拦截功能要来检查里面的数据，看用户是否已经登录
        //key为token
        String token = UUID.randomUUID().toString();
        //存入redis中，过期时间为30分钟·
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
        //6.登录成功返回token后续请求资源时需要携带着token来查看用户是否已经登录,和logininfo前端页面展示当前登录用户
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",token);
        //再将信息返回前端之前需要将密码和盐值置空，以防信息泄露
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo",logininfo);
        return ResultAjax.me().setResult(map);
    }

}
